From 7854bd1b6e6102aff426bf6a48606c4a9b12b7c2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 22 Dec 2006 17:46:32 +0000 Subject: [PATCH] Add functions to allow threadsafe handling of idles and timeouts wrt. to 2006-12-22 Matthias Clasen * gdk/gdk.symbols: * gdk/gdk.h: * gdk/gdk.c: Add functions to allow threadsafe handling of idles and timeouts wrt. to the GDK lock. (#321886, Chris Wilson) --- ChangeLog | 8 + docs/reference/ChangeLog | 5 + docs/reference/gdk/gdk-docs.sgml | 3 + docs/reference/gdk/gdk-sections.txt | 4 + gdk/gdk.c | 235 ++++++++++++++++++++++++++++ gdk/gdk.h | 16 ++ gdk/gdk.symbols | 4 + 7 files changed, 275 insertions(+) diff --git a/ChangeLog b/ChangeLog index 02ff9dcb45..f7afb996ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-12-22 Matthias Clasen + + * gdk/gdk.symbols: + * gdk/gdk.h: + * gdk/gdk.c: Add functions to allow threadsafe handling + of idles and timeouts wrt. to the GDK lock. (#321886, + Chris Wilson) + 2006-12-22 Matthias Clasen * gdk/gdkpango.c: Use pango_cairo_show_error_underline. diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 0d59523d79..9b4e8b732c 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,8 @@ +2006-12-22 Matthias Clasen + + * gdk/gdk-sections.txt: Add new functions. + * gdk/gdk-docs.sgml: Add a "Since 2.12" index. + Thu Dec 14 15:48:14 2006 Tim Janik * gtk/gtk-sections.txt: added new docs. diff --git a/docs/reference/gdk/gdk-docs.sgml b/docs/reference/gdk/gdk-docs.sgml index 6d83d0c6f0..806ec85cee 100644 --- a/docs/reference/gdk/gdk-docs.sgml +++ b/docs/reference/gdk/gdk-docs.sgml @@ -107,5 +107,8 @@ Index of new symbols in 2.10 + + Index of new symbols in 2.12 + diff --git a/docs/reference/gdk/gdk-sections.txt b/docs/reference/gdk/gdk-sections.txt index dc89f088b6..ffc2d98d97 100644 --- a/docs/reference/gdk/gdk-sections.txt +++ b/docs/reference/gdk/gdk-sections.txt @@ -953,6 +953,10 @@ gdk_threads_enter gdk_threads_leave gdk_threads_mutex gdk_threads_set_lock_functions +gdk_threads_add_idle +gdk_threads_add_idle_full +gdk_threads_add_timeout +gdk_threads_add_timeout_full gdk_threads_lock diff --git a/gdk/gdk.c b/gdk/gdk.c index 64be2ff903..11cea84602 100644 --- a/gdk/gdk.c +++ b/gdk/gdk.c @@ -46,6 +46,16 @@ struct _GdkPredicate gpointer data; }; +typedef struct _GdkThreadsDispatch GdkThreadsDispatch; + +struct _GdkThreadsDispatch +{ + GSourceFunc func; + gpointer data; + GDestroyNotify destroy; +}; + + /* Private variable declarations */ static int gdk_initialized = 0; /* 1 if the library is initialized, @@ -448,6 +458,231 @@ gdk_threads_set_lock_functions (GCallback enter_fn, gdk_threads_unlock = leave_fn; } +static gboolean +gdk_threads_dispatch (gpointer data) +{ + GdkThreadsDispatch *dispatch = data; + gboolean ret = FALSE; + + GDK_THREADS_ENTER (); + + if (!g_source_is_destroyed (g_main_current_source ())) + ret = dispatch->func (dispatch->data); + + GDK_THREADS_LEAVE (); + + return ret; +} + +static void +gdk_threads_dispatch_free (gpointer data) +{ + GdkThreadsDispatch *dispatch = data; + + if (dispatch->destroy && dispatch->data) + dispatch->destroy (dispatch->data); + + g_slice_free (GdkThreadsDispatch, data); +} + + +/** + * gdk_threads_add_idle_full: + * @priority: the priority of the idle source. Typically this will be in the + * range btweeen #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE + * @function: function to call + * @data: data to pass to @function + * @notify: function to call when the idle is removed, or %NULL + * + * Adds a function to be called whenever there are no higher priority + * events pending. If the function returns %FALSE it is automatically + * removed from the list of event sources and will not be called again. + * + * This variant of g_idle_add_full() calls @function with the GDK lock + * held. It can be thought of a MT-safe version for GTK+ widgets for the + * following use case, where you have to worry about idle_callback() + * running in thread A and accessing @self after it has been finalized + * in thread B: + * + * + * static gboolean idle_callback (gpointer data) + * { + * SomeWidget *self = data; + * + * /* do stuff with self */ + * + * self->idle_id = 0; + * + * return FALSE; + * } + * + * static void some_widget_do_stuff_later (SomeWidget *self) + * { + * self->idle_id = g_idle_add (idle_callback, self) + * } + * + * static void some_widget_finalize (GObject *object) + * { + * SomeWidget *self = SOME_WIDGET(object); + * + * if (self->idle_id) + * g_source_remove (self->idle_id); + * + * G_OBJECT_CLASS (parent_class)->finalize (object); + * } + * + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 2.12 + */ +guint +gdk_threads_add_idle_full (gint priority, + GSourceFunc function, + gpointer data, + GDestroyNotify notify) +{ + GdkThreadsDispatch *dispatch; + + g_return_val_if_fail (function != NULL, 0); + + dispatch = g_slice_new (GdkThreadsDispatch); + dispatch->func = function; + dispatch->data = data; + dispatch->destroy = notify; + + return g_idle_add_full (priority, + gdk_threads_dispatch, + dispatch, + gdk_threads_dispatch_free); +} + +/** + * gdk_threads_add_idle: + * @function: function to call + * @data: data to pass to @function + * + * A wrapper for the common usage of gdk_threads_add_idle_full() + * assigning the default priority, #G_PRIORITY_DEFAULT_IDLE. + * + * See gdk_threads_add_idle_full(). + * + * Since: 2.12 + */ +guint +gdk_threads_add_idle (GSourceFunc function, + gpointer data) +{ + return gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, + function, data, NULL); +} + + +/** + * gdk_threads_add_timeout_full: + * @priority: the priority of the timeout source. Typically this will be in the + * range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE. + * @interval: the time between calls to the function, in milliseconds + * (1/1000ths of a second) + * @function: function to call + * @data: data to pass to @function + * @notify: function to call when the timeout is removed, or %NULL + * + * Sets a function to be called at regular intervals holding the GDK lock, + * with the given priority. The function is called repeatedly until it + * returns %FALSE, at which point the timeout is automatically destroyed + * and the function will not be called again. The @notify function is + * called when the timeout is destroyed. The first call to the + * function will be at the end of the first @interval. + * + * Note that timeout functions may be delayed, due to the processing of other + * event sources. Thus they should not be relied on for precise timing. + * After each call to the timeout function, the time of the next + * timeout is recalculated based on the current time and the given interval + * (it does not try to 'catch up' time lost in delays). + * + * This variant of g_timeout_add_full() can be thought of a MT-safe version + * for GTK+ widgets for the following use case: + * + * + * static gboolean timeout_callback (gpointer data) + * { + * SomeWidget *self = data; + * + * /* do stuff with self */ + * + * self->timeout_id = 0; + * + * return FALSE; + * } + * + * static void some_widget_do_stuff_later (SomeWidget *self) + * { + * self->timeout_id = g_timeout_add (timeout_callback, self) + * } + * + * static void some_widget_finalize (GObject *object) + * { + * SomeWidget *self = SOME_WIDGET(object); + * + * if (self->timeout_id) + * g_source_remove (self->timeout_id); + * + * G_OBJECT_CLASS (parent_class)->finalize (object); + * } + * + * + * Return value: the ID (greater than 0) of the event source. + * + * Since: 2.12 + */ +guint +gdk_threads_add_timeout_full (gint priority, + guint interval, + GSourceFunc function, + gpointer data, + GDestroyNotify notify) +{ + GdkThreadsDispatch *dispatch; + + g_return_val_if_fail (function != NULL, 0); + + dispatch = g_slice_new (GdkThreadsDispatch); + dispatch->func = function; + dispatch->data = data; + dispatch->destroy = notify; + + return g_timeout_add_full (priority, + interval, + gdk_threads_dispatch, + dispatch, + gdk_threads_dispatch_free); +} + +/** + * gdk_threads_add_timeout: + * @interval: the time between calls to the function, in milliseconds + * (1/1000ths of a second) + * @function: function to call + * @data: data to pass to @function + * + * A wrapper for the common usage of gdk_threads_add_timeout_full() + * assigning the default priority, #G_PRIORITY_DEFAULT. + * + * See gdk_threads_add_timeout_full(). + * + * Since: 2.12 + */ +guint +gdk_threads_add_timeout (guint interval, + GSourceFunc function, + gpointer data) +{ + return gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT, + interval, function, data, NULL); +} + + G_CONST_RETURN char * gdk_get_program_class (void) { diff --git a/gdk/gdk.h b/gdk/gdk.h index 7bec453a98..861198d474 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -193,6 +193,22 @@ void gdk_threads_init (void); void gdk_threads_set_lock_functions (GCallback enter_fn, GCallback leave_fn); +guint gdk_threads_add_idle_full (gint priority, + GSourceFunc function, + gpointer data, + GDestroyNotify notify); +guint gdk_threads_add_idle (GSourceFunc function, + gpointer data); +guint gdk_threads_add_timeout_full (gint priority, + guint interval, + GSourceFunc function, + gpointer data, + GDestroyNotify notify); +guint gdk_threads_add_timeout (guint interval, + GSourceFunc function, + gpointer data); + + #ifdef G_THREADS_ENABLED # define GDK_THREADS_ENTER() G_STMT_START { \ if (gdk_threads_lock) \ diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index 34047da837..94d977b6c9 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -133,6 +133,10 @@ gdk_threads_enter gdk_threads_init gdk_threads_leave gdk_threads_set_lock_functions +gdk_threads_add_idle +gdk_threads_add_idle_full +gdk_threads_add_timeout +gdk_threads_add_timeout_full #endif #endif -- 2.30.2